package com.msy.mai.launch.web.strategy.impl;

import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.msy.mai.common.core.entity.domain.model.LoginUser;
import com.msy.mai.common.core.enums.GrantType;
import com.msy.mai.common.core.enums.LoginType;
import com.msy.mai.common.core.enums.UserStatus;
import com.msy.mai.common.core.exception.user.CaptchaException;
import com.msy.mai.common.core.exception.user.CaptchaExpireException;
import com.msy.mai.common.core.exception.user.UserBlockedException;
import com.msy.mai.common.core.exception.user.UserNotExistException;
import com.msy.mai.common.core.utils.StringUtils;
import com.msy.mai.framework.json.core.utils.JsonUtils;
import com.msy.mai.framework.redis.core.utils.RedisUtils;
import com.msy.mai.framework.satoken.core.utils.LoginHelper;
import com.msy.mai.framework.web.configuration.properties.CaptchaProperties;
import com.msy.mai.launch.web.entity.model.PasswordLoginBody;
import com.msy.mai.launch.web.entity.vo.UserLoginVO;
import com.msy.mai.launch.web.service.SysLoginService;
import com.msy.mai.launch.web.strategy.TokenGranterStrategy;
import com.msy.mai.module.system.entity.vo.SysClientVo;
import com.msy.mai.module.system.entity.vo.SysUserVo;
import com.msy.mai.module.system.service.SysUserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;

import static com.msy.mai.common.core.constants.CacheConst.CAPTCHA_KEY;

/**
 * 密码认证 token 颁发策略
 * @author Wang
 * @version 1.0
 * @Date 2024/10/27 下午5:02
 */
@Slf4j
@RequiredArgsConstructor
public class PasswordTokenGranterStrategy implements TokenGranterStrategy {

    @Value("${msy.security.salt}")
    private String SALT;

    private final CaptchaProperties captchaProperties;

    private final SysUserService sysUserService;

    private final SysLoginService sysLoginService;

    @Override
    public String getKey() {
        return GrantType.PASSWORD.getKey();
    }

    @Override
    public UserLoginVO login(String body, SysClientVo client) {
        PasswordLoginBody loginBody = JsonUtils.parseObject(body,PasswordLoginBody.class);

        String username = loginBody.getUsername();
        String password = loginBody.getPassword();
        String uuid = loginBody.getUuid();
        String code = loginBody.getCode();

        // 验证码校验
        if (captchaProperties.getEnable()){
            validateCaptcha(username,uuid,code);
        }

        SysUserVo userVo = loadByUsername(username);
        // 校验登录状态
        sysLoginService.checkLogin(LoginType.PASSWORD,username,()->!DigestUtil.md5Hex(password.concat(SALT)).equals(userVo.getPassword()));

        // 此处可根据登录用户的数据不同 自行创建 loginUser
        LoginUser loginUser = sysLoginService.buildLoginUser(userVo);

        loginUser.setClientKey(client.getClientKey());
        loginUser.setDeviceType(client.getDeviceType());

        SaLoginModel model = new SaLoginModel();
        model.setDevice(client.getDeviceType());
        // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
        // 例如: 后台用户30分钟过期 app用户1天过期
        model.setTimeout(client.getTimeout());
        model.setActiveTimeout(client.getActiveTimeout());
        model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId());
        // 生成token
        LoginHelper.login(loginUser, model);

        UserLoginVO loginVo = new UserLoginVO();
        loginVo.setToken(StpUtil.getTokenValue());
        loginVo.setExpireIn(StpUtil.getTokenTimeout());
        loginVo.setClientId(client.getClientId());
        return loginVo;
    }

    private SysUserVo loadByUsername(String username) {
        SysUserVo userVo = sysUserService.loadUserVoByUsername(username);

        if (userVo == null){
            throw new UserNotExistException();
        }

        if (UserStatus.DISABLE.getCode().equals(userVo.getStatus())){
            throw new UserBlockedException();
        }

        return userVo;
    }

    /**
     * 验证码校验
     * @param username 用户名
     * @param uuid     验证码唯一表示
     * @param code     验证码
     */
    private void validateCaptcha(String username,String uuid, String code) {
        String captchaKey = CAPTCHA_KEY + StringUtils.defaultString(uuid,"");
        String captchaCache = RedisUtils.getCacheObject(captchaKey);
        RedisUtils.deleteObject(captchaKey);

        if (captchaCache == null){
            // todo 记录登录日志
            throw new CaptchaExpireException();
        }

        if (captchaCache.equalsIgnoreCase(code)){
            // todo 记录登录日志
            throw new CaptchaException();
        }
    }


}
